<?php

namespace Swof\Routing;

use Swof\Contracts\Routing\Registrar;
use Swof\Http\Request;

/**
 * Note:
 *
 * @author:  Zhao Zhiqiang
 * @since:   2021/5/22 18:47
 * @version: 1.0
 *
 * @package Swof\Routing
 */
class Router implements Registrar
{
    public static $instance = null;

    /**
     * The IoC container instance.
     *
     * @var \Swof\Container\Container
     */
    protected $container;

    /**
     * 路由集合实例
     *
     * @var RouteCollection
     */
    protected $routes;

    /**
     * @var \Swof\Routing\Route|null
     */
    protected $current;

    /**
     * 支持的路由动词
     *
     * @var array|string[]
     */
    public static $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];

    /**
     * 路由映射
     *
     * @var array
     */
    protected $routesMap = [];

    /**
     * Router constructor.
     * @throws \Exception
     */
    private function __construct()
    {
        $this->routesMap = [
            'web' => app()->basePath().'/routes/web.php',
            'web_socket' => app()->basePath('routes/web_socket.php')
        ];

        $this->routes = new RouteCollection();
    }

    /**
     * Note: 查询实例
     *
     * @return Router
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:56
     */
    public static function instance(): Router
    {
        if (is_null(self::$instance)) {
            self::$instance = new static();
        }

        return self::$instance ;
    }

    /**
     * Note: 注册 get 路由
     *
     * @param string $uri
     * @param callable|array|string $action
     * @return Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:30
     */
    public function get(string $uri, $action): Route
    {
        return $this->addRoute(['GET', 'HEAD'], $uri, $action);
    }

    /**
     * Note: 注册 post 路由
     *
     * @param string $uri
     * @param callable|array|string $action
     * @return Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:31
     */
    public function post(string $uri, $action): Route
    {
        return $this->addRoute('POST', $uri, $action);
    }

    /**
     * Note: 注册 put 路由
     *
     * @param string $uri
     * @param callable|array|string $action
     * @return Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:34
     */
    public function put(string $uri, $action): Route
    {
        return $this->addRoute('PUT', $uri, $action);
    }

    /**
     * Note: 注册 delete 路由
     *
     * @param string $uri
     * @param callable|array|string $action
     * @return Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:34
     */
    public function delete(string $uri, $action): Route
    {
        return $this->addRoute('DELETE', $uri, $action);
    }

    /**
     * Note: 注册 patch 路由
     *
     * @param string $uri
     * @param callable|array|string $action
     * @return Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:35
     */
    public function patch(string $uri, $action): Route
    {
        return $this->addRoute('PATCH', $uri, $action);
    }

    /**
     * Note: 注册 options 路由
     *
     * @param string $uri
     * @param callable|array|string $action
     * @return Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:36
     */
    public function options(string $uri, $action): Route
    {
        return $this->addRoute('OPTIONS', $uri, $action);
    }

    /**
     * Note: 注册一个新的路由
     *
     * @param array|string $methods
     * @param string $uri
     * @param callable|array|string $action
     * @return Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:38
     */
    public function match($methods, string $uri, $action): Route
    {
        return $this->addRoute(array_map('strtoupper', (array)$methods), $uri, $action);
    }

    /**
     * Note: 注册所有路由
     *
     * @param $uri
     * @param $action
     * @return \Swof\Routing\Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:39
     */
    public function any($uri, $action): Route
    {
        return $this->addRoute(self::$verbs, $uri, $action);
    }

    /**
     * Note: web socket 添加
     *
     * @param string $uri
     * @param string $action
     * @return Route|void
     * @author: Zhao Zhiqiang
     * @since: 2021/5/30 17:07
     */
    public function ws(string $uri, string $action): Route
    {
        $methods = ['OPEN', 'MESSAGE', 'CLOSE'];

        foreach ($methods as $method) {
            $route = $this->addRoute(
                [$method],
                $uri,
                $action . '@' . strtolower($method)
            );
        }

        return $route;
    }

    public function setMethod(string $method)
    {

    }

    /**
     * Note: 注册路由
     *
     * @param $methods
     * @param $uri
     * @param $action
     * @return \Swof\Routing\Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/15 14:44
     */
    protected function addRoute($methods, $uri, $action): Route
    {
        return $this->routes->add($this->createRoute($methods, $uri, $action));
    }

    /**
     * Note: \Swof\Routing\Route 实例
     *
     * @param $methods
     * @param $uri
     * @param $action
     * @return \Swof\Routing\Route
     * @author: Zhao Zhiqiang
     * @since: 2021/5/22 18:14
     */
    protected function createRoute($methods, $uri, $action): Route
    {
        return (new Route($methods, $this->prefix($uri), $action));
    }

    /**
     * Note: 给定前缀
     *
     * @param $uri
     * @return string
     * @author: Zhao Zhiqiang
     * @since: 2021/5/25 16:16
     */
    protected function prefix($uri): string
    {
        return trim($uri, '/') ?: '/';
    }

    /**
     * Note: 引入路由文件
     *
     * @return $this
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:54
     */
    public function registerRoute(): Router
    {
        foreach ($this->routesMap as $key => $path) {
            require_once $path;
        }

        return $this;
    }

    /**
     * Note: 查询路由
     *
     * @return RouteCollection
     * @author: Zhao Zhiqiang
     * @since: 2021/5/16 8:48
     */
    public function getRoutes(): RouteCollection
    {
        return $this->routes;
    }

    /**
     * Note: 分发路由
     *
     * @param Request $request
     * @return void
     * @throws \Exception
     * @since: 2021/5/26 14:58
     * @author: Zhao Zhiqiang
     */
    public function dispatchToRoute(Request $request, array $array = [])
    {
        return $this->runRoute($request, $this->findRoute($request), $array);
    }

    /**
     * Note: 查询匹配当前路由的 Route
     *
     * @param $request
     * @return \Swof\Routing\Route|null
     * @author: Zhao Zhiqiang
     * @since: 2021/5/25 16:36
     */
    protected function findRoute($request): ?Route
    {
        $this->current = $route = $this->routes->match($request);

        return $route;
    }

    /**
     * Note: 执行路由
     *
     * @param Request $request
     * @param \Swof\Routing\Route|null $route
     * @return mixed
     * @throws \Exception
     * @author: Zhao Zhiqiang
     * @since: 2021/5/26 14:58
     */
    protected function runRoute(Request $request, ?Route $route, array $array = [])
    {
        if (is_null($route)) return null;

        return $route->run($array);
    }
}